#include "unit_utils.hpp"
#include "offsets.hpp"
#include "main.hpp"

namespace perf_boost {

char *UnitGetName(uintptr_t *unit) {
    if (!unit) {
        return nullptr;
    }

    auto const GetUnitName = reinterpret_cast<CGUnitGetNameT>(Offsets::CGUnitGetUnitName);
    return GetUnitName(unit, 0);
}

C3Vector UnitGetPosition(uintptr_t *unit) {
    C3Vector result = {};

    if (unit == 0 || ((int) unit & 1) != 0) {
        return result;
    }

    uint32_t memberFunctions = *reinterpret_cast<uint32_t *>(unit);
    if (memberFunctions == 0 || (memberFunctions & 1) != 0) {
        return result;
    }

    uint32_t getPositionPtr = *reinterpret_cast<uint32_t *>(memberFunctions + 0x14);
    if (getPositionPtr == 0 || (getPositionPtr & 1) != 0) {
        return result;
    }

    auto getPositionFn = reinterpret_cast<CGUnitGetPositionT>(getPositionPtr);
    getPositionFn(unit, &result);

    return result;
}

OBJECT_TYPE_ID UnitGetType(uintptr_t *unit) {
    return *reinterpret_cast<OBJECT_TYPE_ID *>(unit + 5);
}

uint64_t UnitGetGuid(uintptr_t *unit) {
    if (!unit) {
        return 0;
    }

    uint64_t guid = *reinterpret_cast<uint64_t *>(unit + 12);
    return guid;
}

uint32_t UnitGetLevel(uintptr_t *unit) {
    if (!unit) {
        return false;
    }

    auto *unitFields = *reinterpret_cast<UnitFields **>(unit + 68);

    if (unitFields == nullptr) {
        // we don't have attribute info.
        return -1;
    }

    return unitFields->level;
}

bool UnitIsDead(uintptr_t *unit) {
    if (!unit) {
        return false;
    }

    auto *unitFields = *reinterpret_cast<UnitFields **>(unit + 68);

    if (unitFields == nullptr) {
        // we don't have attribute info.
        return false;
    }

    auto health = unitFields->health;
    auto dynamicFlags = unitFields->dynamicFlags;

    if (health < 1 || (dynamicFlags & 0x20) != 0) {
        return true;
    } else {
        return false;
    }
}

bool UnitIsControlledByPlayer(uintptr_t *unit) {
    if (!unit) {
        return false;
    }

    auto *unitFields = *reinterpret_cast<UnitFields **>(unit + 68);
    if (unitFields == nullptr) {
        // we don't have attribute info.
        return false;
    }

    auto flags = unitFields->flags;

    if ((flags & UNIT_FLAG_PLAYER_CONTROLLED) != 0) {
        return true;
    } else {
        return false;
    }
}

bool UnitIsInCombat(uintptr_t *unit) {
    if (!unit) {
        return false;
    }

    auto *unitFields = *reinterpret_cast<UnitFields **>(unit + 68);

    if (unitFields == nullptr) {
        // we don't have attribute info.
        return false;
    }

    auto flags = unitFields->flags;
    if ((flags & UNIT_FLAG_IN_COMBAT) != 0) {
        return true;
    } else {
        return false;
    }
}

bool UnitCanAttackUnit(uintptr_t *unit1, uintptr_t *unit2) {
    if (unit1 == nullptr || unit2 == nullptr) {
        return false;
    }

    auto canAttackFn = reinterpret_cast<CGUnitCanAttackT>(Offsets::CGUnitCanAttack);
    return canAttackFn(unit1, unit2);
}

bool UnitIsCharmed(uintptr_t *unit) {
    if (!unit) {
        return false;
    }

    auto *unitFields = *reinterpret_cast<UnitFields **>(unit + 68);

    if (unitFields == nullptr) {
        // we don't have attribute info.
        return false;
    }

    return unitFields->charmedBy != 0;
}

bool UnitIsPvpFlagged(uintptr_t *unit) {
    if (!unit) {
        return false;
    }

    auto *unitFields = *reinterpret_cast<UnitFields **>(unit + 68);

    if (unitFields == nullptr) {
        // we don't have attribute info.
        return false;
    }

    auto flags = unitFields->flags;
    return (flags & UNIT_FLAG_PVP);
}

uint64_t UnitGetTarget(uintptr_t *unit) {
    if (!unit) {
        return 0;
    }

    auto *unitFields = *reinterpret_cast<UnitFields **>(unit + 68);

    if (unitFields == nullptr) {
        return 0;
    }

    return unitFields->target;
}

}